package Eyjafjalla.patch;

import Eyjafjalla.effect.MarkFireBreathEffect;
import Eyjafjalla.helper.StringHelper;
import Eyjafjalla.modcore.ClassEnum;
import Eyjafjalla.modcore.EyjafjallaTag;
import Eyjafjalla.modcore.OtherEnum;
import Eyjafjalla.panel.CloudEnergyPanel;
import Eyjafjalla.power.FireMarkPower;
import basemod.ReflectionHacks;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.evacipated.cardcrawl.modthespire.lib.*;
import com.megacrit.cardcrawl.actions.GameActionManager;
import com.megacrit.cardcrawl.actions.unique.RestoreRetainedCardsAction;
import com.megacrit.cardcrawl.actions.utility.NewQueueCardAction;
import com.megacrit.cardcrawl.actions.utility.UnlimboAction;
import com.megacrit.cardcrawl.cards.AbstractCard;
import com.megacrit.cardcrawl.cards.CardGroup;
import com.megacrit.cardcrawl.cards.CardQueueItem;
import com.megacrit.cardcrawl.cards.DescriptionLine;
import com.megacrit.cardcrawl.characters.AbstractPlayer;
import com.megacrit.cardcrawl.core.CardCrawlGame;
import com.megacrit.cardcrawl.core.EnergyManager;
import com.megacrit.cardcrawl.core.OverlayMenu;
import com.megacrit.cardcrawl.core.Settings;
import com.megacrit.cardcrawl.dungeons.AbstractDungeon;
import com.megacrit.cardcrawl.localization.UIStrings;
import com.megacrit.cardcrawl.monsters.AbstractMonster;
import com.megacrit.cardcrawl.powers.AbstractPower;
import com.megacrit.cardcrawl.random.Random;
import com.megacrit.cardcrawl.vfx.ThoughtBubble;

import java.util.ArrayList;
import java.util.Iterator;

public class CloudPatch {

    public static CardGroup cloudGroup;
    private static final UIStrings uiStrings;
    public static AbstractCard heartCard;

    public static Random shuffleCopy = null;

    public static void initCloudEnergy(){

    }

    public static void createCloudFullDialog(){
        AbstractDungeon.effectList.add(new ThoughtBubble(AbstractDungeon.player.dialogX, AbstractDungeon.player.dialogY, 3.0F, uiStrings.TEXT[3], true));
    }

    public static void clearCloud(){
        cloudGroup.clear();
    }

    public static void drawCardToCloud(int numCards){
        AbstractPlayer p = AbstractDungeon.player;
        for(int i =0;i<numCards;i++){
            AbstractCard c = p.drawPile.getTopCard();
            c.current_x = CardGroup.DRAW_PILE_X;
            c.current_y = CardGroup.DRAW_PILE_Y;
            c.setAngle(0.0F, true);
            c.drawScale = 0.12F;
            c.targetDrawScale = 0.75F;
            c.triggerWhenDrawn();
            addToTop(c);
            p.drawPile.removeTopCard();
        }
    }

    public static void drawCardToCloud(){
        if(cloudGroup.size()==5){
            createCloudFullDialog();
        }
        else {
            CardCrawlGame.sound.playAV("CARD_DRAW_8", -0.12F, 0.25F);
            drawCardToCloud(1);
        }
    }

    public static void glowCheck(){cloudGroup.glowCheck();}
    public static void renderHand(SpriteBatch sb,AbstractCard except){
        cloudGroup.renderHand(sb,except);
    }
    public static void update(){cloudGroup.update();}
    public static void updateLogic(){cloudGroup.updateHoverLogic();}
    public static void renderTip(SpriteBatch sb){
        cloudGroup.renderTip(sb);
    }
    public static void applyPowers(){cloudGroup.applyPowers();}
    public static void hoverCardPush(AbstractCard c){
        if(cloudGroup.size()<1)
            return;
        AbstractCard tmp;
        boolean noMoreLeft=false,noMoreRight=false;
        tmp = cloudGroup.group.get(0);
        if(tmp.target_x>100F*Settings.scale+c.target_x)
            noMoreRight = true;
        tmp = cloudGroup.group.get(cloudGroup.group.size()-1);
        if(tmp.target_x<-100F*Settings.scale+c.target_x)
            noMoreLeft = true;
        for(int i =0;i<cloudGroup.size();i++){
            tmp = cloudGroup.group.get(i);
            if(tmp!=c){
                tmp.target_y+=60F*Settings.scale;
                if(!noMoreLeft&&tmp.target_x<c.target_x){
                    tmp.target_x-=80F*Settings.scale;
                }
                else if(!noMoreRight&&tmp.target_x>=c.target_x){
                    tmp.target_x+=80F*Settings.scale;
                }
            }
        }
    }

    public static void addToTop(AbstractCard c){
        if(c!=null){
            if(AbstractDungeon.player.hoveredCard == c){
                AbstractDungeon.player.releaseCard();
            }
            AbstractCard sc = specialOnCloud(c);
            sc.untip();
            sc.unhover();
            sc.stopGlowing();
            sc.lighten(true);
            CloudField.inCloud.set(sc,true);
            cloudGroup.addToTop(sc);
        }
    }

    public static void addToBottom(AbstractCard c){
        if(c!=null){
            if(AbstractDungeon.player.hoveredCard == c){
                AbstractDungeon.player.releaseCard();
            }
            AbstractCard sc = specialOnCloud(c);
            sc.untip();
            sc.unhover();
            sc.stopGlowing();
            sc.lighten(true);
            CloudField.inCloud.set(sc,true);
            cloudGroup.addToBottom(c);
        }
    }

    public static void resetBeforeRemove(AbstractCard c){
        cloudGroup.removeCard(c);
        CloudField.inCloud.set(c,false);
    }

    public static void moveToCloud(CardGroup from,AbstractCard c){
        if(c!=null){
            if(AbstractDungeon.player.hoveredCard == c){
                AbstractDungeon.player.releaseCard();
            }
            AbstractDungeon.actionManager.removeFromQueue(c);
            if(from!=null)
                from.removeCard(c);
            AbstractCard sc = specialOnCloud(c);
            CloudField.inCloud.set(sc,true);
            sc.untip();
            sc.unhover();
            sc.stopGlowing();
            sc.lighten(true);
            sc.setAngle(0F);
            sc.drawScale = 0.12F;
            sc.targetDrawScale = 0.75F;
            if(from!=null) {
                if (from.type == CardGroup.CardGroupType.DRAW_PILE) {
                    sc.current_x = CardGroup.DRAW_PILE_X;
                    sc.current_y = CardGroup.DRAW_PILE_Y;
                }
                else if(from.type== CardGroup.CardGroupType.DISCARD_PILE){
                    sc.current_x = CardGroup.DISCARD_PILE_X;
                    sc.current_y = CardGroup.DISCARD_PILE_Y;
                }
            }
            cloudGroup.addToTop(sc);
            refreshCloudLayout();
            applyPowers();
        }
    }

    public static void moveToCloud(AbstractCard c){
        moveToCloud(null,c);
    }

    private static AbstractCard specialOnCloud(AbstractCard cloud){
//        if(cloud.cardID.equals(Cloud_Eyjafjalla.ID)){
//            AbstractCard tmp = AbstractDungeon.returnTrulyRandomCardInCombat().makeCopy();
//            StringHelper.addProject(tmp);
//            if(cloud.upgraded&&tmp.cost>=0){
//                tmp.updateCost(-1);
//            }
//            tmp.current_x = cloud.current_x;
//            tmp.current_y = cloud.current_y;
//            tmp.drawScale = cloud.drawScale;
//            tmp.angle = cloud.angle;
//            AbstractDungeon.effectList.add(new ExhaustCardEffect(cloud.makeStatEquivalentCopy()));
//            return tmp;
//        }
        return cloud;
    }

    public static void refreshCloudLayout(){
        if(AbstractDungeon.getCurrRoom().monsters==null||!AbstractDungeon.getCurrRoom().monsters.areMonstersBasicallyDead()){
            float angleRange = 30F - (float) (10-cloudGroup.size()) *3F;
            float incrementAngle = angleRange / (float) cloudGroup.size();
            float sinkStart = (float) Settings.HEIGHT - 220F* Settings.scale;
            float sinkRange = 200F* Settings.scale;
            float incrementSink = sinkRange/ (float) cloudGroup.size()/2F;
            int middle = cloudGroup.size()/2;

            for(int i =0;i<cloudGroup.size();i++){
                //plan 2
                cloudGroup.group.get(i).setAngle(angleRange/2F - incrementAngle*(float) i - incrementAngle/2F);
                //cloudGroup.group.get(i).setAngle(0F);
                int t = i-middle;
                if(t>=0){
                    if(cloudGroup.size()%2==0){
                        t++;
                        t=-t;
                    }else {
                        t=-t;
                    }
                }

                if(cloudGroup.size()%2==0){
                    t++;
                }

                t=(int)((float)t*1.7F);
                //plan 2
                cloudGroup.group.get(i).target_y = sinkStart+incrementSink*(float) t;
                //cloudGroup.group.get(i).target_y = sinkStart;
                cloudGroup.group.get(i).targetDrawScale = 0.6F;
            }

            float midSize = (float)Settings.WIDTH/2F - 10F *Settings.scale;
            //float midSize = AbstractDungeon.player.hb.cX;
            float midImg = AbstractCard.IMG_WIDTH_S * 0.8F;

            switch (cloudGroup.size()){
                case 0:
                    return;
                case 1:
                    cloudGroup.group.get(0).target_x = midSize;
                    break;
                case 2:
                    cloudGroup.group.get(0).target_x = midSize-midImg*0.47F;
                    cloudGroup.group.get(1).target_x = midSize+midImg*0.53F;
                    break;
                case 3:
                    cloudGroup.group.get(0).target_x = midSize-midImg*0.9F;
                    cloudGroup.group.get(1).target_x = midSize;
                    cloudGroup.group.get(2).target_x = midSize+midImg*0.9F;

                    cloudGroup.group.get(0).target_y += 20F*Settings.scale;
                    cloudGroup.group.get(2).target_y += 20F*Settings.scale;
                    break;
                case 4:
                    cloudGroup.group.get(0).target_x = midSize-midImg*1.36F;
                    cloudGroup.group.get(1).target_x = midSize-midImg*0.46F;
                    cloudGroup.group.get(2).target_x = midSize+midImg*0.46F;
                    cloudGroup.group.get(3).target_x = midSize+midImg*1.36F;

                    cloudGroup.group.get(1).target_y -= 10F*Settings.scale;
                    cloudGroup.group.get(2).target_y -= 10F*Settings.scale;
                    break;
                case 5:
                    cloudGroup.group.get(0).target_x = midSize-midImg*1.7F;
                    cloudGroup.group.get(1).target_x = midSize-midImg*0.9F;
                    cloudGroup.group.get(2).target_x = midSize;
                    cloudGroup.group.get(3).target_x = midSize+midImg*0.9F;
                    cloudGroup.group.get(4).target_x = midSize+midImg*1.7F;

                    cloudGroup.group.get(0).target_y += 25F*Settings.scale;
                    cloudGroup.group.get(2).target_y -= 10F*Settings.scale;
                    cloudGroup.group.get(4).target_y += 25F*Settings.scale;
                    break;
                case 6:
                    cloudGroup.group.get(0).target_x = midSize-midImg*2.1F;
                    cloudGroup.group.get(1).target_x = midSize-midImg*1.3F;
                    cloudGroup.group.get(2).target_x = midSize-midImg*0.43F;
                    cloudGroup.group.get(3).target_x = midSize+midImg*0.43F;
                    cloudGroup.group.get(4).target_x = midSize+midImg*1.3F;
                    cloudGroup.group.get(5).target_x = midSize+midImg*2.1F;

                    cloudGroup.group.get(0).target_y += 10F*Settings.scale;
                    cloudGroup.group.get(5).target_y += 10F*Settings.scale;
                    break;
                case 7:
                    cloudGroup.group.get(0).target_x = midSize-midImg*2.4F;
                    cloudGroup.group.get(1).target_x = midSize-midImg*1.7F;
                    cloudGroup.group.get(2).target_x = midSize-midImg*0.9F;
                    cloudGroup.group.get(3).target_x = midSize;
                    cloudGroup.group.get(4).target_x = midSize+midImg*0.9F;
                    cloudGroup.group.get(5).target_x = midSize+midImg*1.7F;
                    cloudGroup.group.get(6).target_x = midSize+midImg*2.4F;

                    cloudGroup.group.get(0).target_y += 25F*Settings.scale;
                    cloudGroup.group.get(1).target_y += 18F*Settings.scale;
                    cloudGroup.group.get(3).target_y -= 6F*Settings.scale;
                    cloudGroup.group.get(5).target_y += 18F*Settings.scale;
                    cloudGroup.group.get(6).target_y += 25F*Settings.scale;
                    break;
                //todo 8 9 10
            }

            AbstractCard card = AbstractDungeon.player.hoveredCard;
            if (card != null) {
                card.setAngle(0.0F);
                card.target_x = (card.current_x + card.target_x) / 2.0F;
                card.target_y = card.current_y;
            }

            Iterator var17 = AbstractDungeon.actionManager.cardQueue.iterator();

            while(var17.hasNext()) {
                CardQueueItem q = (CardQueueItem)var17.next();
                if (q.card != null) {
                    q.card.setAngle(0.0F);
                    q.card.target_x = q.card.current_x;
                    q.card.target_y = q.card.current_y;
                }
            }

            cloudGroup.glowCheck();
        }
    }

    //选择卡牌（修改CardGroup*HAND），统一排在手牌后选中。
    @SpirePatch(clz = CardGroup.class,method = "getHoveredCard")
    public static class HoverCloudPatch{
        @SpirePostfixPatch
        public static AbstractCard Postfix(AbstractCard ret,CardGroup _inst){
            if(ret==null&&!(AbstractDungeon.screen == AbstractDungeon.CurrentScreen.HAND_SELECT&&AbstractDungeon.isScreenUp)&&_inst == AbstractDungeon.player.hand&&AbstractDungeon.topPanel.potionUi.isHidden&&!AbstractDungeon.topPanel.potionUi.targetMode){
                return cloudGroup.getHoveredCard();
            }
            return ret;
        }
    }

    //更新手牌，统一在手牌前更新。
    @SpirePatch(clz = CardGroup.class,method = "update")
    public static class UpdateCloudPatch{
        @SpirePrefixPatch
        public static void Prefix(CardGroup _inst){
            if(AbstractDungeon.player!=null&&_inst == AbstractDungeon.player.hand){
                update();
            }
        }
    }

    //更新手牌LOGIC，统一在手牌前更新。
    @SpirePatch(clz = CardGroup.class,method = "updateHoverLogic")
    public static class UpdateCloudLogicPatch{
        @SpirePrefixPatch
        public static void Prefix(CardGroup _inst){
            if(AbstractDungeon.player!=null&&_inst == AbstractDungeon.player.hand){
                updateLogic();
            }
        }
    }

    //GLOW CHECK
    @SpirePatch(clz = CardGroup.class,method = "glowCheck")
    public static class GlowCheckGroup{
        @SpirePrefixPatch
        public static void Prefix(CardGroup _inst){
            if(AbstractDungeon.player!=null&&_inst == AbstractDungeon.player.hand){
                glowCheck();
            }
        }
    }

    //渲染卡牌（经考虑放在角色渲染前）
    @SpirePatch(clz = AbstractPlayer.class,method = "render")
    public static class RenderCloudPatch{
        @SpirePrefixPatch
        public static void Prefix(AbstractPlayer _inst,SpriteBatch sb){
            if(_inst!=null&&_inst.chosenClass==ClassEnum.Eyjafjalla_CLASS)
                renderHand(sb,_inst.hoveredCard);
        }
    }

    //渲染卡牌TIP
    @SpirePatch(clz = CardGroup.class,method = "renderTip")
    public static class RenderCloudTipPatch{
        @SpirePostfixPatch
        public static void Postfix(CardGroup _inst,SpriteBatch sb){
            if(AbstractDungeon.player!=null&&_inst == AbstractDungeon.player.hand){
                renderTip(sb);
            }
        }
    }

    //重置手牌，用以移动到其他区域时使用
    @SpirePatch(clz = CardGroup.class,method = "resetCardBeforeMoving")
    public static class ResetCloudPatch{
        @SpirePostfixPatch
        public static void Postfix(CardGroup _inst, AbstractCard c){
            if(_inst == AbstractDungeon.player.hand){
                resetBeforeRemove(c);
            }
        }
    }

    //设置云层applyPowers
    @SpirePatch(clz = CardGroup.class,method = "applyPowers")
    public static class ApplyPowersPatch{
        @SpirePostfixPatch
        public static void Postfix(CardGroup _inst){
            if(_inst == AbstractDungeon.player.hand){
                applyPowers();
            }
        }
    }

    //设置布局，统一在手牌更新前更新，如果云层有牌，所有手牌整体下移20PX
    @SpirePatch(clz = CardGroup.class,method = "refreshHandLayout")
    public static class RefreshCloudPatch{
        @SpirePrefixPatch
        public static void Prefix(CardGroup _inst){
            if(_inst == AbstractDungeon.player.hand){
                refreshCloudLayout();
            }
        }
    }

    //HoverPush，选中牌后，把其他牌位置挤开，同时设置选中云层牌时，手牌的正确位置。
    @SpirePatch(clz = CardGroup.class,method = "hoverCardPush")
    public static class PushCloudPatch{
        @SpirePrefixPatch
        public static SpireReturn Prefix(CardGroup _inst,AbstractCard c){
            if(_inst == AbstractDungeon.player.hand){
                //set true Y
                if(cloudGroup.group.contains(c)){
                    c.current_y = (float) Settings.HEIGHT - 325F * Settings.scale;
                    c.target_y = (float) Settings.HEIGHT - 325F * Settings.scale;
                    c.drawScale = 0.9F;
                    c.targetDrawScale = 0.9F;
                    cloudGroup.hoverCardPush(c);

                    //心音悬浮
                    if(HeartPatch.AlwaysHeart ||c.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                        Iterator<AbstractCard> var1 = _inst.group.iterator();
                        while (var1.hasNext()){
                            AbstractCard hand = var1.next();
                            if(HeartPatch.AlwaysHeart ||hand.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                                //由于是手牌，在上面判断是否在云层中时就会不进入，转下文else
                                hand.angle = 0F;
                                hand.targetAngle = 0F;
                                hand.drawScale = 0.9F;
                                hand.targetDrawScale = 0.9F;
                                hand.current_y =AbstractPlayer.HOVER_CARD_Y_POSITION ;
                                hand.target_y = AbstractPlayer.HOVER_CARD_Y_POSITION;
                                _inst.hoverCardPush(hand);
                                break;
                            }
                        }
                    }
                    return SpireReturn.Return();
                }
                else if(AbstractDungeon.player.hoveredCard==c){
                    //如果是可以被炎息标记的牌，抬高预览位置60F
                    if(CloudField.showFire.get(c))
                        c.target_y += 60F*Settings.scale;

                    //仅当是由手牌发起时，即不会无限嵌套
                    if(HeartPatch.AlwaysHeart ||c.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                        Iterator<AbstractCard> var1 = cloudGroup.group.iterator();
                        while (var1.hasNext()){
                            AbstractCard cloud = var1.next();
                            if(HeartPatch.AlwaysHeart ||cloud.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                                cloud.angle = 0F;
                                cloud.targetAngle = 0F;
                                cloud.drawScale = 0.9F;
                                cloud.targetDrawScale = 0.9F;
                                cloud.current_y = (float) Settings.HEIGHT - 270F * Settings.scale;
                                cloud.target_y = (float) Settings.HEIGHT - 270F * Settings.scale;
                                cloudGroup.hoverCardPush(cloud);
                                break;
                            }
                        }
                    }
                }
            }
            return SpireReturn.Continue();
        }
    }

    //云层卡牌选中后位置修正+设置心音牌
    @SpirePatch(clz = AbstractPlayer.class,method = "clickAndDragCards")
    public static class ClickDragPatch{
        @SpirePostfixPatch
        public static boolean Postfix(boolean ret,AbstractPlayer _inst){
            if(!ret)
                return false;
            AbstractCard c = _inst.hoveredCard;
            if(c!=null){
                if(CloudField.inCloud.get(_inst.hoveredCard)){
                    ReflectionHacks.setPrivate(_inst,AbstractPlayer.class,"hoverStartLine",200F*Settings.scale);
                }
                else {
                    //抬高预览位置80F
                    c.target_y += 80F*Settings.scale;
                }
                //总会心音
                boolean travelDream = HeartPatch.AlwaysHeart;
                if(travelDream||c.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                    boolean setNull = true;
                    if(cloudGroup.contains(c)){
                        Iterator<AbstractCard> var1 = _inst.hand.group.iterator();
                        while (var1.hasNext()){
                            AbstractCard hand = var1.next();
                            if(travelDream||hand.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                                heartCard = hand;
                                setNull = false;
                                break;
                            }
                        }
                        if(setNull)
                            heartCard = null;
                    }
                    else if(_inst.hand.contains(c)){
                        Iterator<AbstractCard> var1 = cloudGroup.group.iterator();
                        while (var1.hasNext()){
                            AbstractCard hand = var1.next();
                            if(travelDream||hand.hasTag(EyjafjallaTag.HEART_EYJAFJALLA)){
                                heartCard = hand;
                                setNull = false;
                                break;
                            }
                        }
                        if(setNull)
                            heartCard = null;
                    }
                }
                //EyjafjallaMod.logSomething("====选中牌："+c.name + " ；心音牌："+(heartCard!=null?heartCard.name:"无")+"====");
            }
            return true;
        }
    }

    //云层卡牌的耗能颜色修正
    @SpirePatch(clz = AbstractCard.class,method = "renderEnergy")
    public static class RenderEnergyPatch{
        @SpireInsertPatch(rloc = 31,localvars = {"costColor"})
        public static void Insert(AbstractCard _inst, SpriteBatch sb, @ByRef Color[] costColor){
            if(CloudField.inCloud.get(_inst)){
                if(cloudGroup.contains(_inst)&&!_inst.hasEnoughEnergy()){
                    costColor[0] = new Color(1.0F, 0.3F, 0.3F, 1.0F);
                }
            }
        }
    }

    //固有云层修正
    @SpirePatch(clz = CardGroup.class,method = "initializeDeck")
    public static class InitializeDeckPatch {
        @SpirePrefixPatch
        public static void Prefix(CardGroup _inst,CardGroup masterDeck){
            shuffleCopy = AbstractDungeon.shuffleRng.copy();
        }

        @SpirePostfixPatch
        public static void Postfix(CardGroup _inst, CardGroup masterDeck) {
            if (_inst.type != CardGroup.CardGroupType.DRAW_PILE)
                return;
            clearCloud();
            Iterator<AbstractCard> var1 = _inst.group.iterator();
            while (var1.hasNext()) {
                AbstractCard c = var1.next();
                if (c.hasTag(EyjafjallaTag.INNATE_EYJAFJALLA)&&cloudGroup.size()<5) {
                    addToTop(c);
                    var1.remove();
                }
            }
        }
    }

    //云量更新
    @SpirePatch(clz = OverlayMenu.class,method = "update")
    public static class OverlayUpdatePatch{
        @SpireInsertPatch(rloc = 4)
        public static void Insert(OverlayMenu _inst){
            //目前仅能艾雅法拉更新
            if(AbstractDungeon.player.chosenClass!= ClassEnum.Eyjafjalla_CLASS)
                return;
            CloudEnergyPanel panel = OverlayField.cloudEnergyPanel.get(_inst);
            panel.updatePositions();
            panel.update();
        }
    }

    //云量渲染
    @SpirePatch(clz = OverlayMenu.class,method = "render")
    public static class OverlayRenderPatch{
        @SpireInsertPatch(rloc = 6)
        public static void Insert(OverlayMenu _inst,SpriteBatch sb){
            //目前仅能艾雅法拉渲染
            if(AbstractDungeon.player.chosenClass!= ClassEnum.Eyjafjalla_CLASS)
                return;
            CloudEnergyPanel panel = OverlayField.cloudEnergyPanel.get(_inst);
            panel.render(sb);
        }
    }

    //云量关闭
    @SpirePatch(clz = OverlayMenu.class,method = "hideCombatPanels")
    public static class OverlayHidePatch{
        @SpirePrefixPatch
        public static void Prefix(OverlayMenu _inst){
            CloudEnergyPanel panel = OverlayField.cloudEnergyPanel.get(_inst);
            panel.hide();
        }
    }

    //云量打开
    @SpirePatch(clz = OverlayMenu.class,method = "showCombatPanels")
    public static class OverlayShowPatch{
        @SpirePrefixPatch
        public static void Prefix(OverlayMenu _inst){
            CloudEnergyPanel panel = OverlayField.cloudEnergyPanel.get(_inst);
            panel.show();
        }
    }

    //云量初始化
    @SpirePatch(clz = EnergyManager.class,method = "prep")
    public static class PrepCloudEnergyPatch{
        @SpirePostfixPatch
        public static void Postfix(EnergyManager _inst){
            CloudEnergyPanel.totalCount = 0;
        }
    }

    //云量自动回复
    @SpirePatch(clz = EnergyManager.class,method = "recharge")
    public static class RechargeCloudEnergyPatch{
        @SpirePostfixPatch
        public static void Postfix(EnergyManager _inst){
            CloudEnergyPanel.addEnergy(1);
        }
    }

    private static boolean reasonForEnergy = false;

    //重量级：云量使用的耗能判断
    @SpirePatch(clz = AbstractCard.class,method = "hasEnoughEnergy")
    public static class EnoughCloudEnergyPatch{
        @SpirePrefixPatch
        public static void Prefix(AbstractCard _inst){
            reasonForEnergy = false;
        }

        @SpireInsertPatch(rloc = 35)
        public static void Insert(AbstractCard _inst){
            reasonForEnergy = true;
        }

        @SpirePostfixPatch
        public static boolean Postfix(boolean ret,AbstractCard _inst){
            if(reasonForEnergy&&CloudField.inCloud.get(_inst)){
                if(_inst.costForTurn > CloudEnergyPanel.totalCount&&!_inst.freeToPlay()&&!_inst.isInAutoplay){
                    _inst.cantUseMessage = uiStrings.TEXT[2];
                    return false;
                }
                return true;
            }
            return ret;
        }
    }

    //重量级2：云量耗能相关设置（涉及X费牌及阻止耗能）
    @SpirePatch(clz = AbstractPlayer.class,method = "useCard")
    public static class UseCardEnergyPatch{
        @SpireInsertPatch(rloc = 17)
        public static void Insert1(AbstractPlayer _inst, AbstractCard c, AbstractMonster m,int energyOnUse) {
            if (CloudField.inCloud.get(c)) {
                //初始值，如果这里读到就是freetoplay那么也不耗云量，会添加一个TAG，在UseCardAction中移除
                boolean free = c.freeToPlayOnce;
                if (c.cost == -1 && !c.ignoreEnergyOnUse) {
                    c.energyOnUse = CloudEnergyPanel.totalCount;
                }
                if (c.cost == -1) {
                    c.freeToPlayOnce = true;
                    if (free || c.isInAutoplay)
                        c.tags.add(EyjafjallaTag.FREE_X_EYJAFJALLA);
                }
            }
            //标记为炎息
            if (!c.dontTriggerOnUseCard) {
                AbstractPower fm = _inst.getPower(FireMarkPower.POWER_ID);
                if (fm != null) {
                    ((FireMarkPower) fm).onBeforeUseCard(c);
                }
            }
        }

        @SpireInsertPatch(rloc = 29)
        public static SpireReturn Insert2(AbstractPlayer _inst, AbstractCard c, AbstractMonster m,int energyOnUse){
            //在这里处理心音，从云层打出部分的
            AbstractCard heart = CloudField.heartCard.get(c);
            if(heart!=null){
                if(CloudField.inCloud.get(c)){
                    Iterator<AbstractCard> var1 = AbstractDungeon.player.hand.group.iterator();
                    while (var1.hasNext()){
                        AbstractCard h = var1.next();
                        if(h==heart){
                            boolean randomTarget = (m==null||m.isDeadOrEscaped());
                            h.exhaustOnUseOnce = true;
                            var1.remove();
                            _inst.limbo.addToTop(h);
                            AbstractDungeon.getCurrRoom().souls.remove(h);
                            AbstractDungeon.player.hand.refreshHandLayout();
                            if(!randomTarget)
                                AbstractDungeon.actionManager.addToTop(new NewQueueCardAction(h, m, false, true));
                            else {
                                AbstractDungeon.actionManager.addToTop(new NewQueueCardAction(h, true, false, true));
                            }
                            AbstractDungeon.actionManager.addToTop(new UnlimboAction(h));
                        }
                    }
                }
                else {
                    Iterator<AbstractCard> var1 = cloudGroup.group.iterator();
                    while (var1.hasNext()){
                        AbstractCard h = var1.next();
                        if(h==heart){
                            boolean randomTarget = (m==null||m.isDeadOrEscaped());
                            h.exhaustOnUseOnce = true;
                            var1.remove();
                            _inst.limbo.addToTop(h);
                            AbstractDungeon.getCurrRoom().souls.remove(h);
                            CloudPatch.refreshCloudLayout();
                            if(!randomTarget)
                                AbstractDungeon.actionManager.addToTop(new NewQueueCardAction(h, m, false, true));
                            else {
                                AbstractDungeon.actionManager.addToTop(new NewQueueCardAction(h, true, false, true));
                            }
                            AbstractDungeon.actionManager.addToTop(new UnlimboAction(h));
                        }
                    }
                }
                CloudField.heartCard.set(c,null);
            }

            if(CloudField.inCloud.get(c)){
                cloudGroup.removeCard(c);
                if(c.costForTurn>0&&!c.freeToPlay()&&!c.isInAutoplay&& (!_inst.hasPower("Corruption") || c.type != AbstractCard.CardType.SKILL)){
                    CloudEnergyPanel.useEnergy(c.costForTurn);
                }
                else if(c.cost==-1){
                    if(!c.hasTag(EyjafjallaTag.FREE_X_EYJAFJALLA)){
                        CloudEnergyPanel.useEnergy(c.energyOnUse);
                    }
                    else {
                        c.tags.removeIf(t -> t.equals(EyjafjallaTag.FREE_X_EYJAFJALLA));
                    }
                }

                if (!_inst.hand.canUseAnyCard() &&!cloudGroup.canUseAnyCard()&& !_inst.endTurnQueued) {
                    AbstractDungeon.overlayMenu.endTurnButton.isGlowing = true;
                }

                return SpireReturn.Return();
            }

            return SpireReturn.Continue();
        }

        @SpirePostfixPatch
        public static void Postfix(AbstractPlayer _inst, AbstractCard c, AbstractMonster m,int energyOnUse){
            if (CloudPatch.CloudField.isFire.get(c)) {
                AbstractDungeon.topLevelEffectsQueue.add(new MarkFireBreathEffect(c));
            }
        }
    }

    //复制相关：投影的消耗性复制
    @SpirePatch(clz = AbstractCard.class,method = "makeStatEquivalentCopy")
    public static class MakeStatPatch{
        @SpirePostfixPatch
        public static AbstractCard Postfix(AbstractCard ret,AbstractCard _inst){
            if(_inst.hasTag(EyjafjallaTag.PROJECT_EYJAFJALLA))
                ret.tags.add(EyjafjallaTag.PROJECT_EYJAFJALLA);
            ret.exhaust = _inst.exhaust;
            ret.isEthereal = _inst.isEthereal;

            //云层属性会被复制
            if(CloudField.inCloud.get(_inst)){
                CloudField.inCloud.set(ret,true);
            }
            //炎息牌会被复制，此时刷新描述
            if(CloudField.isFire.get(_inst)){
                CloudField.isFire.set(ret,true);
                ret.initializeDescription();
            }
            return ret;
        }
    }

    //这里是RestoreRetainedCardsAction中，额外触发的【保留】部分
    @SpirePatch(clz = RestoreRetainedCardsAction.class,method = "update")
    public static class RetainPatch{
        @SpirePostfixPatch
        public static void Postfix(RestoreRetainedCardsAction _inst){
            //因为它总会return，所里这里会最后执行
            for(AbstractCard c:cloudGroup.group){
                if(c.retain||c.selfRetain){
                    c.onRetained();
                }
            }
        }
    }

    //手牌特殊补充
    @SpirePatch(clz = AbstractPlayer.class,method = "applyStartOfTurnPreDrawCards")
    public static class StartOfTurnPrePatch{
        @SpirePostfixPatch
        public static void Postfix(AbstractPlayer _inst){
            for(AbstractCard c:cloudGroup.group){
                c.atTurnStartPreDraw();
            }
        }
    }

    //这里是触发云层牌回合开始时的补充
    @SpirePatch(clz = AbstractPlayer.class,method = "applyStartOfTurnCards")
    public static class StartOfTurnPatch{
        @SpirePostfixPatch
        public static void Postfix(AbstractPlayer _inst){
            for (AbstractCard c : cloudGroup.group) {
                c.atTurnStart();
            }
            CloudFixPatch.canSetRoundEnd = true;
        }
    }

    //这里是触发云层牌
    @SpirePatch(clz = GameActionManager.class,method = "callEndOfTurnActions")
    public static class EndOfTurnPatch{
        @SpireInsertPatch(rloc = 8)
        public static void Insert(GameActionManager _inst){
            for (AbstractCard c : cloudGroup.group) {
                c.triggerOnEndOfTurnForPlayingCard();
            }
        }
    }


    @SpirePatch(clz = AbstractCard.class,method = SpirePatch.CLASS)
    public static class CloudField{
        public static SpireField<Boolean> inCloud = new SpireField<Boolean>(()->false);
        public static SpireField<AbstractCard> heartCard = new SpireField<AbstractCard>(()->null);
        public static SpireField<Boolean> isFire = new SpireField<Boolean>(()->false);
        public static SpireField<Boolean> showFire = new SpireField<Boolean>(()->false);
        public static SpireField<Float> fireTimer = new SpireField<Float>(()->0F);
        public static SpireField<String> fireDescription = new SpireField<String>(()->"");
        public static SpireField<ArrayList<DescriptionLine>> fireLines = new SpireField<ArrayList<DescriptionLine>>(()->new ArrayList<>());
    }

    @SpirePatch(clz = OverlayMenu.class,method = SpirePatch.CLASS)
    public static class OverlayField{
        public static SpireField<CloudEnergyPanel> cloudEnergyPanel = new SpireField<CloudEnergyPanel>(()->new CloudEnergyPanel());
    }

    static {
        cloudGroup = new CardGroup(OtherEnum.HAND_CLOUD);
        uiStrings = CardCrawlGame.languagePack.getUIString(StringHelper.CLOUD_ENERGY);
    }
}
